package com.ddwl.common.interceptor;

import com.ddwl.common.annotation.Auth;
import com.ddwl.common.annotation.Submit;
import com.ddwl.common.constant.BaseEnum;
import com.ddwl.common.constant.RedisKey;
import com.ddwl.common.exception.CommonException;
import com.ddwl.common.exception.GlobalException;
import com.ddwl.common.exception.IllegalException;
import com.ddwl.common.service.AuthService;
import com.ddwl.common.service.RedisService;
import com.ddwl.common.util.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author Fu Zhaohui
 */
@Slf4j
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {
    @Resource
    private AuthService authService;
    @Resource
    private RedisService redisService;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            Class<?> clazz = method.getDeclaringClass();

            Submit submit = method.getAnnotation(Submit.class);
            if (submit != null) {
                String ak = request.getHeader("ak");
                if (StringUtils.hasText(ak)) {
                    String methodName = handlerMethod.getBeanType().getTypeName() + "." + method.getName();
                    String key = RedisKey.SUBMIT_KEY.getKey(methodName, ak);
                    if (redisService.getValueOps().getOperations().hasKey(key)) {
                        throw new CommonException("亲，您已操作过，请勿重复操作");
                    }
                    redisService.getValueOps().set(key, System.currentTimeMillis() + "");
                    redisService.getValueOps().getOperations().expire(key, 3, TimeUnit.SECONDS);
                }
            }

            Auth auth = Optional.ofNullable(method.getAnnotation(Auth.class)).orElse(clazz.getAnnotation(Auth.class));
            if (auth != null) {
                String product = request.getHeader("pk");
                BaseEnum.ProductEnum productEnum = BaseEnum.ProductEnum.getProduct(product);
                if (productEnum == null) {
                    log.info("访问IP地址 =》 {}", HttpUtil.getIp(request));
                    throw new IllegalException();
                }
                //如果是API来的请求跳过权限校验
                if(product.contains(BaseEnum.ProductEnum.API.getName())){
                    return true;
                }
                BaseEnum.ProductEnum[] products = auth.product();
                if (products != null && products.length > 0) {
                    List<String> temps = new ArrayList<>(products.length);
                    for (BaseEnum.ProductEnum temp : products) {
                        temps.add(temp.getName());
                    }
                    if (!temps.contains(product)) {
                        log.info("权限未开通 -> {}, {}", request.getHeader("ak"), product);
                        throw new GlobalException(403,"接口权限未开放");
                    }
                }
                Map<String, String> authMap;
                // 没有声明需要权限,或者声明不验证权限
                if (auth.required()) {
                    authMap = authService.auth(request);
                    if (authMap == null) {
                        return false;
                    }
                } else {
                    authMap = authService.preAuth(request);
                    if (authMap == null) {
                        return false;
                    }
                }
                request.setAttribute("authMap", authMap);
            }
        }
        return true;
    }
}